/*
 * Created on May 8, 2004
 *
 * Vectrics Inc.  GNU Public License.
 */
package org.vectrics.hibernate;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.vectrics.SystemException;

/**
 * @author mmoore
 */
public class Filter  {
	
	private transient final Log log = LogFactory.getLog(this.getClass());
	private Collection fields = new ArrayList();
	private Collection froms = new ArrayList();
	private Collection<String> orderBys = new ArrayList<String>();
	private boolean noSelect = false;
	private Selector selector = new SelectorImpl();
	private List<String> innerJoin = new ArrayList<String>();
	private List<String> leftJoin = new ArrayList<String>();
	
	/**
	 * 
	 */
	public Filter() {
		super();
	}
	
	public void addFrom(String objectName, String instanceName) {
		if (!hasFrom(objectName, instanceName))
			froms.add(objectName + " " + instanceName);
	}
	
	public boolean hasFrom(String objectName, String instanceName) {
		boolean result = false;
		
		result = froms.contains(objectName + " " + instanceName);
		
		return(result);
	}

	public void setConjunction(short value) {
		selector.setConjuntion(value);
	}
	
	public Selector getSelector() {
		return(selector);
	}
	
	private String getFromSectionStatement() {
		String fieldSection = "";
		Iterator iter = froms.iterator();
		while (iter.hasNext()) {
			String field = (String)iter.next();
			String aliasForm = field.split(" ")[1];
			fieldSection += field;
			 
			for (String inner: this.innerJoin){
				String aliasInner = inner.split(" ")[0];
				String property = inner.split(" ")[1];
				if (aliasInner.trim().equals(aliasForm)) {
					fieldSection += " inner join "+aliasForm+"."+property+" as "+property+" ";					
				}
			}
			
			for (String left: this.leftJoin){
				String aliasLeft = left.split(" ")[0];
				String property = left.split(" ")[1];
				if (aliasLeft.trim().equals(aliasForm)) {
					fieldSection += " left join "+aliasForm+"."+property+" as "+property+" ";					
				}
			}
			
			if (iter.hasNext()) {
				fieldSection += ", ";
			}
		}
		if (fieldSection.trim().length() <= 0)
			fieldSection = null;
		
		if (log.isDebugEnabled()) {
			log.debug("From section = " + fieldSection);
		}
		return(fieldSection);
	}
	
	public void addOrderBy(String field) {
		orderBys.add(field);
	}
	
	private String getOrderBysSection() {
		String fieldSection = "";
		Iterator iter = orderBys.iterator();
		while (iter.hasNext()) {
			String field = (String)iter.next();
			fieldSection += field;
			if (iter.hasNext()) {
				fieldSection += ", ";
			}
		}
		if (fieldSection.trim().length() <= 0)
			fieldSection = null;
		return(fieldSection);
	}
	
	public void addColumnToColumnComparison(String column1, String operator, String column2) {
	    this.selector.addColumnToColumn(column1, operator, column2);
	}
	
	public void addParameterCondition(String field, String operator, Date date) {
		this.selector.addParameterCondition(field, operator, date);
	}
	
	public void addParameterCondition(String field, String operator, Long param) {
		this.selector.addParameterCondition(field, operator, param);
	}

	public void addParameterCondition(String field, String operator, String param) {
		this.selector.addParameterCondition(field, operator, param);
	}
	
	public void addParameterCondition(String field, Boolean value) {
		this.selector.addParameterCondition(field, value);
	}
	
	public Query createQuery(Session session) throws HibernateException {
		String statement = this.getStatement();
		if (log.isDebugEnabled()) {
			log.debug("createQuery.statement = " + statement);
		}
		Query query = session.createQuery(statement);
		
		if (this.selector.getElements().size() == 1) {
			Object element = this.selector.getElements().iterator().next();
			if (element instanceof Selector) {
				log.debug("The only element of this selector is a child selector, make the child the new main selector");
				this.selector = (Selector)element;
			}
		}
		
		this.selector.addParametersToQuery(query);
		return(query);
	}
	
	/*
	public Object[] getQueryObjectArray() {
		Object[] objects = new Object[this.parameters.size()];
		for (int i = 0; i < parameters.size(); i++) {
			objects[i] = ((Parameter)parameters.get(i)).object;
		}
		return(objects);
	}
	*/
	
	public void addCondition(String condition) {
		selector.addCondition(condition);
	}
	
	public void addStringCondition(String field, String value) {
		String sqlValue = value;
		boolean patternMatchUsed = false;
		while (sqlValue.indexOf("*") >= 0) {
			patternMatchUsed = true;
			int pos = sqlValue.indexOf("*");
			String prefix = "";
			String suffix = "";
			if (pos > 0) {
				prefix = sqlValue.substring(0, pos);
			}
			sqlValue += "%";
			if (pos != (value.length()  - 1)) {
				suffix = sqlValue.substring(pos + 1);
			}
			sqlValue = prefix + "%" + suffix;
		}
		
		if (patternMatchUsed) {
			this.addCondition(field + " like '" + sqlValue + "'");
		}
		else {
			this.addCondition(field + " = '" + sqlValue + "'");
		}
	}
	
	public void addSelectField(String field) {
		fields.add(field);
	}
	
	private String getSelectedFieldSection() {
		String fieldSection = "";
		Iterator iter = fields.iterator();
		if (iter.hasNext() == false)
			throw new SystemException("Filter is missing selected fields - use addSelectField() method");
		while (iter.hasNext()) {
			String field = (String)iter.next();
			fieldSection += field;
			if (iter.hasNext()) {
				fieldSection += ", ";
			}
		}
		if (fieldSection.trim().length() <= 0)
			fieldSection = null;
		return(fieldSection);
	}
	
	private String getConditionSection() {
		String conditionSection = null;

		if (this.selector.getElements().size() > 0) {
			conditionSection = this.selector.getStatement();
			if (conditionSection.trim().length() <= 0)
				conditionSection = null;
		}
		log.debug("Conditions section: " + conditionSection);
		return(conditionSection);
	}
	
	public String getStatement() throws SqlFilterNotValidException {
		StringBuffer statement = new StringBuffer();
		if (this.noSelect == false) {
			String fieldSection = getSelectedFieldSection();
			if (fieldSection == null)
				throw new SqlFilterNotValidException("Missing fields");
	
			statement.append("select ");
			statement.append(fieldSection);
		}
		statement.append(" from");
		if (froms.size() == 0)
			throw new SqlFilterNotValidException("No 'from' clauses were added to the filter");
		statement.append(" ");
		statement.append(this.getFromSectionStatement());
		
		String conditionsSection = this.getConditionSection();
		if (conditionsSection != null) {
			statement.append(" where ");
			statement.append(conditionsSection);
		}
		
		if (orderBys.size() > 0) {
			statement.append(" order by ");
			statement.append(this.getOrderBysSection());
		}
		return(statement.toString());
	}

	/**
	 * @return Returns the noSelect.
	 */
	public boolean getNoSelect() {
		return noSelect;
	}

	/**
	 * @param noSelect The noSelect to set.
	 */
	public void setNoSelect(boolean noSelect) {
		this.noSelect = noSelect;
	}

	/**
	 * Metodo para generar un inner Join
	 * @param from
	 * @param property
	 */
	public void addInnerJoin(String from, String property) {
		this.innerJoin.add(from+" "+property);
	}

	public void addLeftJoin(String from, String property) {
		this.leftJoin.add(from+" "+property);
	}

}
